// Binary Files (二进制文件) 示例
// 演示 Pascal 中二进制文件操作的各种用法
program p_23_binary_files;

{$mode objfpc}{$H+}
// {$mode objfpc} 启用 Object Pascal 模式
// {$H+} 启用长字符串支持（AnsiString）

// 引入单元文件
uses
  SysUtils;  // 系统工具单元，包含常用的系统函数

// 定义学生记录类型
type
  TStudent = record
    Name: array[0..49] of Char;  // 固定长度字符串
    Age: Integer;
    Score: Real;
  end;

// 将字符串转换为固定长度字符数组的辅助过程
procedure StringToCharArray(source: String; var dest: array of Char);
var
  i: Integer;
begin
  for i := 0 to High(dest) do
    dest[i] := #0;  // 初始化为空字符
  
  for i := 1 to Length(source) do
  begin
    if i > High(dest) + 1 then
      Break;
    dest[i - 1] := source[i];
  end;
end;

// 将固定长度字符数组转换为字符串的辅助函数
function CharArrayToString(source: array of Char): String;
var
  i: Integer;
begin
  Result := '';
  for i := 0 to High(source) do
  begin
    if source[i] = #0 then
      Break;
    Result := Result + source[i];
  end;
end;

// 写入学生数据到二进制文件的过程
procedure WriteStudentsToBinaryFile(filename: String);
var
  fileVar: File;
  students: array[1..3] of TStudent;
  bytesWritten: Integer;
begin
  // 初始化学生数据
  StringToCharArray('张三', students[1].Name);
  students[1].Age := 20;
  students[1].Score := 85.5;
  
  StringToCharArray('李四', students[2].Name);
  students[2].Age := 19;
  students[2].Score := 92.0;
  
  StringToCharArray('王五', students[3].Name);
  students[3].Age := 21;
  students[3].Score := 78.5;
  
  // 创建并打开二进制文件用于写入
  Assign(fileVar, filename);
  Rewrite(fileVar, 1);  // 以1字节为单位打开文件
  
  // 写入学生数据
  for var i := 1 to 3 do
  begin
    BlockWrite(fileVar, students[i], SizeOf(TStudent), bytesWritten);
    WriteLn('  写入学生记录 ', i, '，写入字节数: ', bytesWritten);
  end;
  
  // 关闭文件
  Close(fileVar);
  WriteLn('  已将学生数据写入二进制文件: ', filename);
end;

// 从二进制文件读取学生数据的过程
procedure ReadStudentsFromBinaryFile(filename: String);
var
  fileVar: File;
  student: TStudent;
  bytesRead: Integer;
  recordNum: Integer = 1;
begin
  // 打开二进制文件用于读取
  Assign(fileVar, filename);
  Reset(fileVar, 1);  // 以1字节为单位打开文件
  
  WriteLn('  从二进制文件读取学生数据:');
  // 读取学生数据
  while not Eof(fileVar) do
  begin
    BlockRead(fileVar, student, SizeOf(TStudent), bytesRead);
    if bytesRead = SizeOf(TStudent) then
    begin
      WriteLn('    记录 ', recordNum, ':');
      WriteLn('      姓名: ', CharArrayToString(student.Name));
      WriteLn('      年龄: ', student.Age);
      WriteLn('      分数: ', student.Score:0:1);
      Inc(recordNum);
    end;
  end;
  
  // 关闭文件
  Close(fileVar);
end;

// 演示二进制文件的随机访问
procedure DemonstrateRandomAccess(filename: String);
var
  fileVar: File;
  student: TStudent;
  bytesRead: Integer;
begin
  // 打开二进制文件用于读取
  Assign(fileVar, filename);
  Reset(fileVar, 1);
  
  WriteLn('  演示随机访问 - 读取第2条记录:');
  
  // 定位到第2条记录（跳过第1条记录）
  Seek(fileVar, SizeOf(TStudent));  // 文件指针定位到第2条记录
  
  // 读取第2条记录
  BlockRead(fileVar, student, SizeOf(TStudent), bytesRead);
  if bytesRead = SizeOf(TStudent) then
  begin
    WriteLn('    姓名: ', CharArrayToString(student.Name));
    WriteLn('    年龄: ', student.Age);
    WriteLn('    分数: ', student.Score:0:1);
  end;
  
  // 关闭文件
  Close(fileVar);
end;

// 写入和读取原始字节数据
procedure DemonstrateRawBytes(filename: String);
var
  fileVar: File;
  data: array[0..9] of Byte;
  bytesWritten, bytesRead: Integer;
  i: Integer;
begin
  WriteLn('  演示原始字节数据操作:');
  
  // 初始化数据
  for i := 0 to 9 do
    data[i] := i * 10;
  
  // 写入字节数据
  Assign(fileVar, filename);
  Rewrite(fileVar, 1);
  BlockWrite(fileVar, data, SizeOf(data), bytesWritten);
  Close(fileVar);
  WriteLn('    已写入 ', bytesWritten, ' 字节数据');
  
  // 读取字节数据
  Reset(fileVar, 1);
  BlockRead(fileVar, data, SizeOf(data), bytesRead);
  Close(fileVar);
  WriteLn('    已读取 ', bytesRead, ' 字节数据');
  
  Write('    数据内容: ');
  for i := 0 to 9 do
    Write(data[i], ' ');
  WriteLn;
end;

// 程序主入口
begin
  WriteLn('=== Pascal 二进制文件操作示例 ===');
  WriteLn;
  
  // 1. 创建并写入二进制文件
  WriteLn('1. 创建并写入二进制文件:');
  WriteStudentsToBinaryFile('students.dat');
  
  WriteLn;
  
  // 2. 从二进制文件读取数据
  WriteLn('2. 从二进制文件读取数据:');
  ReadStudentsFromBinaryFile('students.dat');
  
  WriteLn;
  
  // 3. 二进制文件的随机访问
  WriteLn('3. 二进制文件的随机访问:');
  DemonstrateRandomAccess('students.dat');
  
  WriteLn;
  
  // 4. 原始字节数据操作
  WriteLn('4. 原始字节数据操作:');
  DemonstrateRawBytes('bytes.dat');
  
  WriteLn;
  
  // 5. 文件信息检查
  WriteLn('5. 文件信息检查:');
  var testFile: String = 'students.dat';
  if FileExists(testFile) then
  begin
    var fileVar: File;
    Assign(fileVar, testFile);
    Reset(fileVar, 1);
    WriteLn('  文件 ''', testFile, ''' 大小: ', FileSize(fileVar), ' 字节');
    Close(fileVar);
  end
  else
    WriteLn('  文件 ''', testFile, ''' 不存在');
  
  WriteLn;
  
  // 6. 清理 - 删除示例文件
  WriteLn('6. 清理:');
  var filesToDelete: array[1..2] of String = ('students.dat', 'bytes.dat');
  for var i := 1 to 2 do
  begin
    if FileExists(filesToDelete[i]) then
    begin
      var fileVar: File;
      Assign(fileVar, filesToDelete[i]);
      Erase(fileVar);
      WriteLn('  已删除文件: ', filesToDelete[i]);
    end;
  end;
  
  WriteLn;
  WriteLn('演示了 Pascal 中二进制文件操作的各种用法');
  
  // 等待用户按键，防止程序窗口立即关闭
  ReadLn;
end.